package com.hz.cheetah.helper;

import com.squareup.javapoet.*;


import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.util.Set;

import static javax.lang.model.SourceVersion.latestSupported;


/**
 * Created by yangchuan on 16/12/8.
 */
//@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.hz.cheetah.helper.Async"})
public class AsyncProcessor extends AbstractProcessor {
    private static final String SUFFIX = "Async";

    private Types typeUtils;
    private Elements elementUtils;
    private Filer filer;
    private Messager messager;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        typeUtils = processingEnv.getTypeUtils();
        elementUtils = processingEnv.getElementUtils();
        filer = processingEnv.getFiler();
        messager = processingEnv.getMessager();
    }

    /**
     * {@inheritDoc}
     *
     * @param annotations
     * @param roundEnv
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager = processingEnv.getMessager();
        filer = processingEnv.getFiler();

        String className = null;
        MethodSpec.Builder method = null;

        for (Element e : roundEnv.getElementsAnnotatedWith(Async.class)) {
            if (e.getKind() != ElementKind.INTERFACE) {
                messager.printMessage(Diagnostic.Kind.ERROR, String.format("Only interface can be annotated with @%s", Async.class.getSimpleName()));
                return true;
            }
            className = e.toString();
            TypeElement superClassName = elementUtils.getTypeElement(className);
            TypeSpec.Builder classBuilder = TypeSpec.interfaceBuilder(e.getSimpleName()+SUFFIX)
                    .addModifiers(Modifier.PUBLIC)
                    .addSuperinterface( TypeName.get(superClassName.asType()));


            for (Element enclosedElement : e.getEnclosedElements()) {
                if(enclosedElement.getKind() == ElementKind.METHOD){

                    ExecutableElement amethod = (ExecutableElement) enclosedElement;

                    method =MethodSpec.methodBuilder(enclosedElement.getSimpleName()+SUFFIX)
                            .addModifiers(Modifier.ABSTRACT)
                            .addModifiers(Modifier.PUBLIC);
                    int i = 0;
                    for(VariableElement ve :amethod.getParameters()) {
                        method.addParameter(TypeName.get(ve.asType()),
                                "var"+ ++i);
                    }
                    method.returns(ParameterizedTypeName.get(ClassName.get("com.google.common.util.concurrent", "ListenableFuture"),
                            TypeName.get(amethod.getReturnType())));
                    classBuilder = classBuilder.addMethod(method.build());
                }
            }


            PackageElement pkg = elementUtils.getPackageOf(superClassName);
            String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();
            if (packageName == null) {
                messager.printMessage(Diagnostic.Kind.ERROR, "@Async find package name is null.");
                return true;
            }

            TypeSpec typeSpec = classBuilder.build();
            // Write file
            try {
                JavaFile.builder(packageName, typeSpec).build().writeTo(filer);
            } catch (IOException e1) {
                messager.printMessage(Diagnostic.Kind.ERROR, e1.getMessage());
                return true;
            }
        }

        return true;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return latestSupported();
    }

}
